package main

import (
    "os"
    "fmt"
    "reflect"
    "runtime"
    "kumachan/standalone/qt"
    "kumachan/standalone/util"
    "kumachan/standalone/util/gcr"
    "kumachan/standalone/util/argv"
    "kumachan/standalone/util/fatal"
    "kumachan/interpreter"
    "kumachan/interpreter/lang/textual"
    "kumachan/interpreter/lang/textual/syntax"
    "kumachan/interpreter/runtime/vm"
    "kumachan/interpreter/runtime/rt"
    "kumachan/interpreter/runtime/lib/env"
    "kumachan/support/atom"
    "kumachan/support/mnfstedit"
)


var Help string
const Version = "0.0.0 pre-alpha"
const NoProgramFilePrompt = "** No Program File or Directory Specified **\n" +
    "input a path or press enter to start REPL:"

type Args struct {
    Positional   [] string  `arg:"positional" hint:"[PATH [ARGUMENT]...]"`
    Command      string     `arg:"command" key:"help-0; version-0; atom-0; mnfst-0; gcr-1; parse; run" default:"run" desc:"show this help; show version; start plugin backend service for the Atom Editor; start module manifest editor; exec another program with GUI-based crash report; parse the file at PATH; run the file or directory at PATH"`
    EnableRepl   bool       `arg:"flag-enable" key:"repl" desc:"enable REPL"`
    DebugUI      bool       `arg:"flag-enable" key:"debug-ui" desc:"enable ui debugging"`
    AsmDumpPath  string     `arg:"value-string" key:"asm-dump" hint:"FILE" desc:"dump assembly code to FILE"`
}

var Commands = map[string] func(*Args) {
    "help": func(_ *Args) {
        fmt.Fprintf(os.Stderr, "%s\n", Help)
    },
    "version": func(_ *Args) {
        fmt.Fprintf(os.Stderr, "%s\n", Version)
    },
    "atom": func(_ *Args) {
        var err = atom.LangServer(os.Stdin, os.Stdout, os.Stderr)
        if err != nil { fatal.CrashGoError(err, "Crashed") }
    },
    "mnfst": WithQt(func(_ *Args) {
        mnfstedit.Setup()
    }),
    "gcr": WithQt(func(args *Args) {
        var another_argv = args.Positional
        gcr.Exec(another_argv)
    }),
    "parse": func(args *Args) {
        const default_root = syntax.DefaultRootPartName
        const repl_root = syntax.ReplRootPartName
        var L = len(args.Positional)
        if L == 0 {
            textual.DebugParser(os.Stdin, "(stdin)", repl_root)
        } else if L >= 1 {
            for _, file := range args.Positional {
                f, err := os.Open(file)
                if err != nil { fatal.CrashGoError(err, "Crashed") }
                textual.DebugParser(f, f.Name(), default_root)
                err = f.Close()
                if err != nil { fatal.CrashGoError(err, "Crashed") }
            }
        }
    },
    "run": WithQt(func(args *Args) {
        var entry_path, no_file_specified, program_args =
            (func() (string, bool, ([] string)) {
                var L = len(args.Positional)
                if L == 0 {
                    return "", true, nil
                } else {
                    var entry_path = args.Positional[0]
                    var program_args = args.Positional[1:]
                    return entry_path, false, program_args
                }
            })()
        if no_file_specified {
            fmt.Fprintf(os.Stderr, "%s\n", NoProgramFilePrompt)
            var line, _, err = util.WellBehavedFscanln(os.Stdin)
            if err != nil { panic(err) }
            if line != "" {
                entry_path = line
                no_file_specified = false
            }
        }
        var options = &vm.Options {
            Environment:      env.NewDefaultEnvironment(program_args),
            ExecutionOptions: vm.ExecutionOptions {},
            DebuggingOptions: vm.DebuggingOptions {
                BasicOptions: rt.DebugOptions {
                    DebugUI: args.DebugUI,
                },
                AsmDumpPath: args.AsmDumpPath,
            },
        }
        var repl_enabled = (args.EnableRepl || no_file_specified)
        var repl_config = interpreter.ReplConfig {
            Input:  os.Stdin,
            Output: os.Stderr,
        }
        var p, err = interpreter.Compile(entry_path)
        if err != nil {
            fatal.Crash(err, "Failed to Compile")
        }
        if repl_enabled {
            interpreter.InjectRepl(&p, repl_config)
        }
        interpreter.Run(p, options)
        os.Exit(0)
    }),
}
func WithQt(cmd func(*Args)) func(*Args) {
    return func(args *Args) {
        qt.Init()
        go cmd(args)
        qt.Main()
    }
}

func main() {
    runtime.LockOSThread()  // ensures Qt event loop running on main thread
    var args Args
    var err error
    Help, err = argv.ParseArgs(os.Args, reflect.ValueOf(&args))
    if err != nil {
        fatal.BadArgs(err, Help)
    }
    Commands[args.Command](&args)
}


